Skip to content

755: Write Codebloom standup bot in Rust#745

Open
tahminator wants to merge 6 commits intomainfrom
755
Open

755: Write Codebloom standup bot in Rust#745
tahminator wants to merge 6 commits intomainfrom
755

Conversation

@tahminator
Copy link
Owner

@tahminator tahminator commented Feb 7, 2026

755

Description of changes

Checklist before review

  • I have done a thorough self-review of the PR
  • Copilot has reviewed my latest changes, and all comments have been fixed and/or closed.
  • If I have made database changes, I have made sure I followed all the db repo rules listed in the wiki here. (check if no db changes)
  • All tests have passed
  • I have successfully deployed this PR to staging
  • I have done manual QA in both dev (and staging if possible) and attached screenshots below.

Screenshots

Dev

image image image image

Staging

image image image

@github-actions
Copy link
Contributor

github-actions bot commented Feb 7, 2026

Available PR Commands

  • /ai - Triggers all AI review commands at once
  • /review - AI review of the PR changes
  • /describe - AI-powered description of the PR
  • /improve - AI-powered suggestions
  • /deploy - Deploy to staging

See: https://github.com/tahminator/codebloom/wiki/CI-Commands

@github-actions
Copy link
Contributor

github-actions bot commented Feb 7, 2026

Title

755: Write Codebloom standup bot in Rust


PR Type

Enhancement, Documentation


Description

  • Add Rust Discord standup bot

  • Schedule Mon/Wed 12pm ET via Redis

  • Send embed; add slash command

  • Env templates and git-crypt config


Diagram Walkthrough

flowchart LR
  A["Tokio interval (15 min)"]
  B["Check Redis last standup"]
  C["Send Discord 'Codebloom Standup' embed"]
  D["Set last standup in Redis"]
  E["No-op"]
  F["Load env credentials"]
  G1["Init Redis client"]
  H1["Init Discord client (background)"]

  A --> B
  B -- "due today?" --> C
  C --> D
  B -- "not due" --> E
  F --> G1
  F --> H1
Loading

File Walkthrough

Relevant files
Configuration changes
5 files
.gitattributes
Encrypt standup-bot production env with git-crypt               
+2/-0     
example.env
Provide development environment variable template               
+9/-0     
example.env.production
Provide production environment variable template                 
+9/-0     
credentials.rs
Load Discord credentials from environment                               
+32/-0   
credentials.rs
Load Redis URI credentials                                                             
+11/-0   
Documentation
2 files
README.md
Introduce internal services directory overview                     
+3/-0     
README
Document Rust Discord standup bot purpose                               
+6/-0     
Dependencies
1 files
Cargo.toml
Create crate and declare dependencies                                       
+12/-0   
Enhancement
6 files
client.rs
Initialize Discord client and embed sender                             
+54/-0   
ping.rs
Implement hello slash command                                                       
+10/-0   
handlers.rs
Handle interactions and register guild commands                   
+49/-0   
main.rs
Schedule standup checks and dispatch messages                       
+65/-0   
client.rs
Create Redis connection and timestamp helpers                       
+43/-0   
utils.rs
Compute Monday/Wednesday noon ET trigger                                 
+21/-0   
Miscellaneous
3 files
mod.rs
Add commands module wiring                                                             
+1/-0     
mod.rs
Expose Discord submodules                                                               
+4/-0     
mod.rs
Expose Redis submodules                                                                   
+3/-0     
Error handling
1 files
error.rs
Define Redis client error types                                                   
+21/-0   
Additional files
1 files
.env.production [link]   

@github-actions
Copy link
Contributor

github-actions bot commented Feb 7, 2026

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

When no prior standup timestamp exists in Redis, the bot passes the current time to is_time_to_send_standup_message, which makes it think a standup was already sent today, preventing the first message from ever sending. Initialize with a past timestamp or accept Option<DateTime> in the check.

None => {
    if !is_time_to_send_standup_message(Utc::now()) {
        return;
    }
}
Misconfigured Intents

The Discord gateway intents are set to GUILD_MEMBERS only, which is privileged and likely unnecessary. Use minimal required intents (e.g., GUILDS or non_privileged) to reliably receive interactions and reduce permission scope.

static INTENTS: GatewayIntents = GatewayIntents::GUILD_MEMBERS;
static HTTP: OnceLock<Arc<Http>> = OnceLock::new();

pub async fn init_in_bg(discord_creds: &DiscordCredentials) -> Result<(), Error> {
    let mut client = Client::builder(&discord_creds.token, INTENTS)
        .event_handler(Handler)
        .await?;
Staging Config Gap

Only a production env file is encrypted (internal/standup-bot/.env.production). To meet AC (use staging credentials and deploy via Coolify), add a staging env (internal/standup-bot/.env.staging) and ensure Coolify wiring references it; otherwise risk using production creds.

internal/standup-bot/.env.production filter=git-crypt-l3 diff=git-crypt-l3

@tahminator
Copy link
Owner Author

/ai

@tahminator
Copy link
Owner Author

/review

@tahminator
Copy link
Owner Author

/describe

@tahminator
Copy link
Owner Author

/improve

@github-actions
Copy link
Contributor

github-actions bot commented Feb 8, 2026

Title

755: Write Codebloom standup bot in Rust


PR Type

Enhancement, Documentation


Description

  • Add Rust Discord standup bot under internal/.

    • Schedules Mon/Wed 12pm ET checks
    • Persists last run in Redis
  • Build Docker image and publish tags

  • Automate Coolify app creation and deployment

  • Wire CI to build and redeploy


Diagram Walkthrough

flowchart LR
  SRC["standup-bot (Rust)"]
  BUILD["Build image script"]
  IMG["Docker image tags"]
  COOLIFY["Coolify app resource"]
  ENVS["Set envs from .env.production"]
  DEPLOY["Trigger & poll deployment"]
  RUNTIME["Bot runs on 15m interval"]
  DISCORD["Send Discord embed + thread"]
  REDIS["Store last standup timestamp"]

  SRC -- "Dockerfile + Cargo" --> BUILD
  BUILD -- "buildx push/load" --> IMG
  IMG -- "create/update app" --> COOLIFY
  COOLIFY -- "update secrets/envs" --> ENVS
  ENVS -- "deploy" --> DEPLOY
  DEPLOY -- "start container" --> RUNTIME
  RUNTIME -- "Mon/Wed 12pm ET" --> DISCORD
  RUNTIME -- "write timestamp" --> REDIS
Loading

File Walkthrough

Relevant files
Ci
4 files
standup-bot.ts
Buildx Docker image with multi tags                                           
+87/-0   
action.yml
Composite action to build bot image                                           
+38/-0   
action.yml
Composite action to redeploy via Coolify                                 
+32/-0   
ci-cd.yml
Add bot image build and redeploy jobs                                       
+51/-0   
Deployment
3 files
index.ts
Coolify deploy orchestration with polling                               
+117/-0 
utils.ts
Ensure app exists, set envs, deploy                                           
+113/-0 
Dockerfile
Multi-stage Docker build for Rust bot                                       
+15/-0   
Configuration changes
2 files
.gitattributes
Encrypt internal env files with git-crypt                               
+3/-0     
.nvim.lua
Configure rust-analyzer rustfmt nightly args                         
+12/-0   
Dependencies
2 files
package.json
Add Coolify SDK and typecheck hack                                             
+3/-1     
Cargo.toml
Define Rust crate and dependencies                                             
+12/-0   
Documentation
3 files
README.md
Introduce internal apps directory overview                             
+3/-0     
example.env.ci
Example Coolify CI environment variables                                 
+7/-0     
README.md
Document bot purpose and stack                                                     
+6/-0     
Enhancement
6 files
main.rs
Initialize clients and schedule standup loop                         
+56/-0   
utils.rs
Eastern Time schedule and dedupe logic                                     
+32/-0   
client.rs
Redis async client and helpers                                                     
+49/-0   
credentials.rs
Load Redis URI from environment                                                   
+11/-0   
client.rs
Discord init and send embed/thread                                             
+80/-0   
handlers.rs
Slash commands and ready registration                                       
+58/-0   
Additional files
12 files
index.ts +8/-0     
.env.ci [link]   
.env.production [link]   
example.env +9/-0     
example.env.production +9/-0     
rustfmt.toml +6/-0     
hello.rs +12/-0   
mod.rs +1/-0     
credentials.rs +32/-0   
mod.rs +4/-0     
error.rs +21/-0   
mod.rs +3/-0     

@github-actions
Copy link
Contributor

github-actions bot commented Feb 8, 2026

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Incorrect Flag Parsing

DOCKER_UPLOAD is parsed with Boolean(...), so the string "false" is truthy and may cause unintended image pushes. Parse explicitly (e.g., process.env.DOCKER_UPLOAD === "true") and only log a successful push when actually pushing.

const shouldDockerUpload = Boolean(process.env.DOCKER_UPLOAD) || false;
Composite Inputs Bug

The composite action passes secrets to the nested action using secrets.* instead of its declared inputs, which will fail at runtime. Use the declared inputs (e.g., ${{ inputs.GPG_PRIVATE_KEY }}) and remove or define the unused inputs referenced in env.

uses: ./.github/composite/setup-ci
with:
  GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
  GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
Runtime Incompatibility

Building on rust:alpine and running on alpine can yield a glibc-linked binary that won’t run on Alpine; crates may also need system libs (e.g., OpenSSL). Build for MUSL (install and target x86_64-unknown-linux-musl) or switch to a Debian/Ubuntu runtime compatible with the produced binary, and ensure required build/runtime dependencies are installed.

FROM rust:1-alpine AS build

WORKDIR /app

COPY internal/standup-bot/Cargo.toml internal/standup-bot/Cargo.lock ./ 
COPY internal/standup-bot/src ./src
RUN cargo build --release -v

FROM alpine:3.23.3

WORKDIR /app

COPY --from=build /app/target/release/codebloom-standup-bot ./

ENTRYPOINT ["./codebloom-standup-bot"]

Create codebloom-standup-bot, a Rust app that will send our standup message to the Codebloom dev server, backed by a Redis db to store last standup time (to avoid duplications). Setup Dockerfile and required environment variables in production, as necessary.
…lify under internal (DESC)

Coolify is brought in due to the fact that DigitalOcean is expensive and paying 5 dollars a month to deploy one-off script doesn't make a lot of financial sense right now. Instead, these internal/ pkgs will likely be deployed on a VPS that @tahminator owns via Coolify.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant